/*
 *
 * CPascalCompiler
 *
 */
package compiler;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Stack;

import compiler.error.CArrayBoundError;
import compiler.error.CCompilingError;
import compiler.error.CExpectedError;
import compiler.error.CExpectedIdentifierError;
import compiler.error.CNotBooleanExpressionError;
import compiler.error.CNotConstError;
import compiler.error.CNotSimpleVariable;
import compiler.error.CNotTypeError;
import compiler.error.CSyntaxError;
import compiler.error.CTooMuchLevelError;
import compiler.error.CTypeNotMatchError;

class CPascalCompiler implements PVM
{
    static final int TT_EOF = -1;
	static final int TT_EOL = '\n';
	static final int TT_NUMBER = -2;
	static final int TT_WORD = -3;
	static final int TT_CHAR = -4;		//char

	static final int TT_ADD = '+';		// +
	static final int TT_MINUS = '-';	// -
	static final int TT_MUL = '*';		// *
	static final int TT_DIV = '/';		// /
	static final int TT_EQUAL = '=';	// =
	static final int TT_LT = '<';		// <
	static final int TT_GT = '>';		// >
	static final int TT_COLON = ':';	// :
	static final int TT_SMCLN = ';';	// ;
	static final int TT_POINT = '.';	// .
	static final int TT_COMMA = ',';	// .

	static final int TT_ASSIGN = -100;	// :=
	static final int TT_LE = -101;		// <=
	static final int TT_GE = -102;		// >=
	static final int TT_NE = -113;		// <>
	static final int TT_DD = -114;		// ..

	static final int TT_INTNUM = -200;	//Integer number

	static final int TT_RESERVED = 65536;
	static final int TT_AND = TT_RESERVED + 0;
	static final int TT_ARRAY = TT_RESERVED + 1;
	static final int TT_BEGIN = TT_RESERVED + 2;
	static final int TT_CONST = TT_RESERVED + 3;
	static final int TT_DO = TT_RESERVED + 4;
	static final int TT_DOWNTO = TT_RESERVED + 5;
	static final int TT_ELSE = TT_RESERVED + 6;
	static final int TT_END = TT_RESERVED + 7;
	static final int TT_FOR = TT_RESERVED + 8;
	static final int TT_FUNCTION = TT_RESERVED + 9;
	static final int TT_GOTO = TT_RESERVED + 10;
	static final int TT_IF = TT_RESERVED + 11;
	static final int TT_MOD = TT_RESERVED + 12;
	static final int TT_NOT = TT_RESERVED + 13;
	static final int TT_OF = TT_RESERVED + 14;
	static final int TT_OR = TT_RESERVED + 15;
	static final int TT_PROCEDURE = TT_RESERVED + 16;
	static final int TT_PROGRAM = TT_RESERVED + 17;
	static final int TT_RECORD = TT_RESERVED + 18;
	static final int TT_REPEAT = TT_RESERVED + 19;
	static final int TT_THEN = TT_RESERVED + 20;
	static final int TT_TO = TT_RESERVED + 21;
	static final int TT_TYPE = TT_RESERVED + 22;
	static final int TT_UNTIL = TT_RESERVED + 23;
	static final int TT_VAR = TT_RESERVED + 24;
	static final int TT_WHILE = TT_RESERVED + 25;

	private StreamPascalTokenizer its;	//the tokenized stream for Lexical stream
	private DataOutputStream ofs;

	private CSymbolTable symbolTable;	//the symbol table

	private final int MAX_LEVEL = 16;	//the max level for procedure
	private final int GLOBAL_LEVEL = 1;	//the GLOBAL LEVEL
	private int currentLevel = 0;		//the current level
	private int []varOffset = new int[MAX_LEVEL];
								//the variable offset stack

	//the code generation buffer
	private int []codeBuffer = new int[MEM_MAX];
	private int codePointer = 0;		//the code pointer

	//Mini Pascal Compiler Constructor
	//	ifn, ofn :the input and output file name
	//	thorws I/O Exception when I/O errror
	CPascalCompiler() {};

	int Compile(String ifn, String ofn) throws IOException
	{
	   	its = new StreamPascalTokenizer(new FileInputStream(ifn));
		ofs = new DataOutputStream(new FileOutputStream(ofn));
		currentLevel = 0;		//the current level
		codePointer = 0;		//the code pointer
		try
		{
			symbolTable = new CSymbolTable();
			NewLevel();
		}
		catch (CCompilingError ce)
		{
		}
		return HandleProgram();
	}

	//Handle the program (the main operation of the compiler)
	//
	//	<PROGRAM> -> [Program <program name>;] <BLOCK BODY>.
	//
	private int HandleProgram() throws IOException
	{
		int errL = -1;	//Error line number
		try
		{
			varOffset[GLOBAL_LEVEL] = 3;
				//Save [0] for dynamic link
				//Save [1] for static link
				//Save [2] for return Addr
			int progbegin = GetQuad();
			Emit(OP_PROG);
			int varLenAddr = GetQuad();
			Emit(0);		//VarLength wait for calc later
			Emit(100);		//TempLength maxnum 100
			int progDispAddr = GetQuad();
			Emit(0);		//Displace wait for feed back;
			Emit(GetLineNo());
			if (its.GetToken() == TT_PROGRAM)
			{
				its.GetIdentifier();
				symbolTable.Register(its.GetString(), new CProgram());
				its.GetSign(TT_SMCLN);
			}
			else its.pushBack();
			HandleDefineBlock();
			codeBuffer[varLenAddr] = varOffset[GLOBAL_LEVEL] - 3;
			codeBuffer[progDispAddr] = codePointer - progbegin;
			HandleCompound();
			Emit(OP_EPROG);
			its.GetSign(TT_POINT);
			WriteToDisk();
			ofs.close();
		}
		catch (CCompilingError ce)
		{
			errL = GetLineNo();
			System.err.println("Error at line " + errL +": " + ce.getMessage());
		}

		return errL;
	}

	//Get current line number of the parsered file
	//	return (int)
	private int GetLineNo()	{ return its.lineno(); }

	void WriteToDisk() throws IOException
	{
		byte[] cr = new byte[2];
		cr[0] = 13;
		cr[1] = 10;
		for (int i = 0; i <= codePointer; i++)
		{
			ofs.writeBytes(Integer.toString(codeBuffer[i]));
			ofs.write(cr, 0, 2);
		}
	}

	//Enter the new level
	private void NewLevel() throws CCompilingError
	{
		currentLevel ++;
		if (currentLevel == MAX_LEVEL) throw new CTooMuchLevelError();
		varOffset[currentLevel] = 0;
		symbolTable.NewLevel();
	}

	//Leave the current level
	private void FreeLevel() throws CCompilingError
	{
		symbolTable.RemoveCurrentLevel();
		currentLevel --;
	}

	//the block mode constant
	private final int BM_UNKNOWN = 0;
	private final int BM_CONST = 1;
	private final int BM_VAR = 2;
	private final int BM_TYPE = 3;
	private final int BM_PROC = 4;
	private final int BM_COMPOUND = 5;

	//detect the next block mode (See above)
	private int DetectBlockMode() throws IOException, CCompilingError
	{
		switch (its.GetToken())
		{
		case TT_CONST: return BM_CONST;
		case TT_VAR: return BM_VAR;
		case TT_TYPE: return BM_TYPE;
		case TT_PROCEDURE: return BM_PROC;
		case TT_BEGIN: return BM_COMPOUND;
		}
		return BM_UNKNOWN;
	}

	//Analyze the block body
	//
	//	<BLOCK BODY> -> { const <CONST BLOCK> [{<CONST BLOCK>}] |
	//					  type <TYPE BLOCK> [{<TYPE BLOCK>}] |
	//					  var <VAR BLOCK> [{<TYPE BLOCK>}] |
	//					  procedure <PROC BLOCK> [{<PROC BLOCK>}]
	//					} <COMPOUND STATEMENT>
	//
	private void HandleDefineBlock() throws IOException, CCompilingError
	{
		int currentMode = DetectBlockMode();
		int mode;
		if (currentMode == BM_UNKNOWN) throw new CSyntaxError();
		while (true)
		{
			switch (currentMode)
			{
			case BM_CONST:
				HandleConstBlock();
				break;
			case BM_VAR:
				HandleVarBlock();
				break;
			case BM_PROC:
				HandleProcBlock();
				break;
			case BM_TYPE:
				HandleTypeBlock();
				break;
			case BM_COMPOUND:
				return;
			}
			mode = DetectBlockMode();
			if (mode != BM_UNKNOWN) currentMode = mode; else its.pushBack();
		}
	}

	//Get an integer type value from the tokenized stream
	//
	//	<INT VALUE> -> [+|-]<INT NUMBER> | <INT CONST>
	//
	private int GetIntVal() throws IOException, CCompilingError
	{
		CSymbol cs;
		its.GetToken();
		int i = 0;
		boolean nflag = false;
		if (its.ttype == '-')
		{
			nflag = true; its.GetToken();
		};
		if (its.ttype == TT_WORD)
		{
			cs = symbolTable.GetInfo(its.GetString());
			if (!(cs instanceof CIntConst)) throw new CNotConstError(its.GetString());
			i = ((CIntConst)cs).val;
		}
		else if (its.ttype == TT_INTNUM)
		{
			i = its.ival;
		}
		else throw new CSyntaxError();
		return (nflag)? -i: i;
	}

	//Analyze the constant definiens block
	//
	//	<CONST BLOCK> -> <IDENTIFIER> = <CONST VALUE> | <CONST IDENTIFIER>
	//
	private void HandleConstBlock() throws IOException, CCompilingError
	{
		its.GetIdentifier();
		String name = its.GetString();
		its.GetSign(TT_EQUAL);
		CConst cs = null;
		its.GetToken();
		boolean nflag = false;
		if (its.ttype == '-')
		{
			nflag = true;
			its.GetToken();
		};

		if (its.ttype == TT_WORD)
		{
			CSymbol smb = symbolTable.GetInfo(its.GetString());
			if (!(smb instanceof CConst)) throw new CNotConstError(its.GetString());
			cs = (CConst)((CConst)smb).clone();
			cs.level = currentLevel;
			if (nflag && !(smb instanceof CIntConst)) throw new CSyntaxError();
		}
		else
		{
			if (its.ttype == TT_INTNUM)
			{
				if (nflag) its.ival = - its.ival;
				cs = new CIntConst(currentLevel, its.ival);
			}
			else throw new CSyntaxError();

		}

		symbolTable.Register(name, cs);
		its.GetSign(TT_SMCLN);
	}

	//Analyze the type definiens block
	//
	//	<TYPE BLOCK> -> <IDENTIFIER> = <TYPE>;
	//
	private void HandleTypeBlock() throws IOException, CCompilingError
	{
		its.GetIdentifier();
		String name = its.GetString();
		its.GetSign(TT_EQUAL);
		CType ct = HandleType();
		symbolTable.Register(name, ct);
		its.GetSign(TT_SMCLN);
	}

	//Analyze the type specification
	//
	//	<TYPE> -> <TYPE NAME> | Array '[' <ARRAY TYPE> | Record <RECORD TYPE>
	//
	private CType HandleType() throws IOException, CCompilingError
	{
		CSymbol ts = null;
		switch (its.GetWord())
		{
		case TT_ARRAY:
			its.GetSign('[');
			ts = HandleArrayType();
			break;
		case TT_RECORD:
			ts = HandleRecordType();
			break;
		default:
			if (its.ttype == TT_WORD)
			{
				ts = symbolTable.GetInfo(its.GetString());
				if (!(ts instanceof CType)) throw new CNotTypeError(its.GetString());
				ts  = new CCloneType(currentLevel, (CType)ts);		//Without test
			}
		}
		return (CType)ts;
	}

	//Analyze the type specification
	//
	//	<ARRAY TYPE> -> <INT VALUE>..<INT VALUE> [{, <INT VALUE>..<INT VALUE>}] ']' Of <TYPE>
	//
	private CType HandleArrayType() throws IOException, CCompilingError
	{
		int lo = GetIntVal();
		its.GetSign(TT_DD);
		int hi = GetIntVal();
		if (lo > hi) throw new CArrayBoundError();
		its.GetToken();
		if (its.ttype == TT_COMMA)
			return new CArrayType(currentLevel, lo, hi, HandleArrayType());
		if (its.ttype != ']') throw new CExpectedError("]");
		its.GetSign(TT_OF);
		return new CArrayType(currentLevel, lo, hi, HandleType().GetType());
	}

	//Analyze the record item
	//
	//	<RECORD ITEM> -> <IDENTIFIER> [{, <IDENTIFIER>}] : <TYPE>
	//
	private void HandleRecordItem(CRecordType rt) throws IOException, CCompilingError
	{
		String sa[] = new String[128];
		int count = 0;
		while (true)
		{
			its.GetIdentifier();
			sa[count++] = its.GetString();
			its.GetToken();
			if (its.ttype == TT_COLON) break;
			if (its.ttype != TT_COMMA) throw new CExpectedError(",");
		}

		CType ts = HandleType().GetType();
		for (int i = 0; i < count; i++)	rt.AddItem(sa[i], ts);
	}

	//Analyze the record type
	//
	//	<RECORD TYPE> -> [<RECORD ITEM> [{; <RECORD ITEM>}] [;]] END
	//
	private CType HandleRecordType() throws IOException, CCompilingError
	{
		CRecordType rt = new CRecordType(currentLevel);
		if (its.GetWord() == TT_END) return rt;
		its.pushBack();
		while (true)
		{
			HandleRecordItem(rt);
			its.GetToken();
			if (its.ttype == TT_END) break;
			if (its.ttype != TT_SMCLN) throw new CExpectedError(";");
		}
		return rt;
	}

	//Analyze the variable block
	//
	//	<VAR BLOCK> -> <IDENTIFIER> [{, <IDENTIFIER>}] : <TYPE>
	//
	private void HandleVarBlock() throws IOException, CCompilingError
	{
		String sa[] = new String[128];
		int count = 0;
		while (true)
		{
			its.GetIdentifier();
			sa[count++] = its.GetString();
			its.GetToken();
			if (its.ttype == TT_COLON) break;
			if (its.ttype != TT_COMMA) throw new CExpectedError(",");
		}

		CType ts = HandleType().GetType();
		for (int i = 0; i < count; i++)
		{
			symbolTable.Register(sa[i], new CVariable(currentLevel, varOffset[currentLevel], ts));
			varOffset[currentLevel] += ts.GetSize();
		}
		its.GetSign(TT_SMCLN);
	}

	//Analyze the argument item
	//
	//	<ARGUMENT ITEM> -> [VAR] <IDENTIFIER> [{, <IDENTIFIER>}] : <TYPE>
	//
	private void HandleArgumentItem(CProcedure proc, Stack stack) throws IOException, CCompilingError
	{
		its.GetToken();
		String sa[] = new String[128];
		boolean varTag;
		if (its.ttype == TT_VAR)
			varTag = true;
		else
		{
			its.pushBack();
			varTag = false;
		}
		int count = 0;
		while (true)
		{
			its.GetIdentifier();
			sa[count++] = its.GetString();
			its.GetToken();
			if (its.ttype == TT_COLON) break;
			if (its.ttype != TT_COMMA) throw new CExpectedError(",");
		}
		CType ts = HandleType().GetType();
		CVariable v = null;
		for (int i = 0; i < count; i++)
		{
			proc.AddItem(varTag, ts);
			if (varTag)
			{
				v = new CVarArg(currentLevel, varOffset[currentLevel], ts);
				symbolTable.Register(sa[i], v);
				varOffset[currentLevel] ++;		//Save the variable argument address
			}
			else
			{
				v = new CVariable(currentLevel, varOffset[currentLevel], ts);
				symbolTable.Register(sa[i], v);
				varOffset[currentLevel] += ts.GetSize();
			}
			stack.push(v);
		}
	}

	//Analyze the procedure block
	//
	//	<PROC BLOCK> -> ['(' <ARGUMENT ITEM> {; <ARGUMENT ITEM>} ')'] <BLOCK BODY>
	//
	private void HandleProcBlock() throws IOException, CCompilingError
	{
		its.GetIdentifier();
		String name = its.GetString();
		CProcedure proc = new CProcedure(currentLevel, GetQuad());
		Stack stack = new Stack();
		symbolTable.Register(name, proc);
		NewLevel();
		if (its.GetToken() == '(')
		{
			while (true)
			{
				HandleArgumentItem(proc, stack);
				its.GetToken();
				if (its.ttype == ')') break;
				if (its.ttype != TT_SMCLN) throw new CExpectedError(",");
			}
		}
		else its.pushBack();
		int baseOffset = varOffset[currentLevel];
		varOffset[currentLevel] = 3;
				//Save [0] for dynamic link
				//Save [1] for static link
				//Save [2] for return Addr
		int procbegin = GetQuad();
		Emit(OP_PROC);
		int varLenAddr = GetQuad();
		Emit(0);		//VarLength wait for calc later
		Emit(1000);		//TempLength maxmium 1000
		int progDispAddr = GetQuad();
		Emit(0);		//Displace wait for feed back;
		Emit(GetLineNo());
		CVariable v = null;
		while (!stack.empty())
		{
			v = (CVariable)stack.pop();
			v.offset -= baseOffset;
		}
		its.GetSign(TT_SMCLN);
		HandleDefineBlock();
		codeBuffer[varLenAddr] = varOffset[currentLevel] - 3;
		codeBuffer[progDispAddr] = codePointer - procbegin;
		HandleCompound();
		Emit(OP_EPROC);
		Emit(baseOffset);
		its.GetSign(TT_SMCLN);
		FreeLevel();
	};

	//Emit the code i to code buffer and increase the code pointer
	private void Emit(int i) throws IOException
	{
		codeBuffer[codePointer++] = i;
	}

	//Test if it is simple type (Integer or Boolean)
	private boolean IsSimpleType(CType type)
	{
		return (type instanceof CIntType) || (type instanceof CBooleanType);
	}

	//Emit the instructions that push the variable address into the stack
	//	return the type of the variable
	//
	//	<VARIABLE> -> <IDENTIFIER> [ '[' <ARRAY VARIABLE> ']' | '.' <RECORD VARIABLE> ]
	//
	private CType GetVariableAddress(CVariable v) throws IOException, CCompilingError
	{
		if (v instanceof CVarArg)
		{
			Emit(OP_VARPRM);
			Emit(currentLevel - v.level);
			Emit(v.offset);
		}
		else
		{
			Emit(OP_VARIABLE);
			Emit(currentLevel - v.level);
			Emit(v.offset);
		}

		CType t = v.GetType();
		while (!IsSimpleType(t))
		{
			if (t instanceof CArrayType)
				if (its.GetToken() == '[')
				{
					t = HandleArrayVariable((CArrayType)t);
					its.GetSign(']');
				}
				else
				{
					its.pushBack();
					return t;
				}
			else if (t instanceof CRecordType)
			{
				if (its.GetToken() == '.')
					t = HandleRecordVariable((CRecordType)t);
				else
				{
					its.pushBack();
					return t;
				}
			}
		}
		return t;
	}

	//Emit the instructions that push the variable value into the stack
	//	return the type of the variable
	private CType GetVariableValue(CVariable v) throws IOException, CCompilingError
	{
		CType type = GetVariableAddress(v);
		if (IsSimpleType(type))
			Emit(OP_SMPVAL);
		else
		{
			Emit(OP_VALUE);
			Emit(type.GetSize());
		}
		return type;
	}


	//Analyze the index of the array variable
	//
	//	<ARRAY VARIABLE> -> <EXPRESSION> [, <EXPRESSION>]
	//
	private CType HandleArrayVariable(CArrayType at) throws IOException, CCompilingError
	{
		int k;
		CType type = at.GetBaseType();
		HandleExpression();
		Emit(OP_INDEX);
		Emit(at.lo);
		Emit(at.hi);
		Emit(type.GetSize());
		Emit(GetLineNo());
		its.GetToken();
		if (its.ttype == ',')
			return HandleArrayVariable((CArrayType)type);
		its.pushBack();
		return type;
	}

	//Analyze the field of the record variable
	//
	//	<RECORD VARIABLE> -> <IDENTIFIER>
	//
	private CType HandleRecordVariable(CType v) throws IOException, CCompilingError
	{
		its.GetIdentifier();
		CRecordItem item = ((CRecordType)v).GetItem(its.GetString());
		Emit(OP_FIELD);
		Emit(item.offset);
		return item.GetType();
	}

   	//Analyze arithmetical expression
	//
	//	<SimpleExpression> -> [+|-] <Term> {<AdditionalOperator> <Term>}
	//

	private CType HandleSimpleExpression() throws IOException, CCompilingError
	{
		CType type = null;
		switch (its.GetToken())
		{
		case TT_ADD:
			type = HandleFactor();
			break;
		case TT_MINUS:
			type = HandleFactor();
			Emit(OP_MINUS);
			break;
		case TT_WORD:
		case TT_INTNUM:
		case TT_NOT:
			its.pushBack();
			type = HandleTerm();
			break;
		case '(':
			its.pushBack();
			type = HandleFactor();
			break;
		default:
			its.pushBack();
			return null;
		}
		return type;
	}

    //Analyze term
	//
	//	<Term> -> <Factor> {<MultiplicationalOperator> <Factor>}
	//
	//  <AdditionalOperator> -> + | - | or
	//
	private CType HandleTerm() throws IOException, CCompilingError
	{
		CType leftType = null;
		if ((leftType = HandleFactor()) == null)
			throw new CSyntaxError();
		CType rightType = null;
		switch (its.GetToken())
		{
		case TT_ADD:
			rightType = HandleTerm();
			if (leftType.GetTypeId() != rightType.GetTypeId())
				throw new CTypeNotMatchError();
			Emit(OP_ADD);
			break;
		case TT_MINUS:
			rightType = HandleTerm();
			if (leftType.GetTypeId() != rightType.GetTypeId())
				throw new CTypeNotMatchError();
			Emit(OP_SUB);
			break;
		case TT_OR:
			rightType = HandleTerm();
			if (leftType.GetTypeId() != rightType.GetTypeId())
				throw new CTypeNotMatchError();
			Emit(OP_OR);
			break;
		default:
			its.pushBack();
			break;
		}
		return leftType;
	}

    //
    //<MultiplicationalOperator> -> * | div | and | mod
    //
    private CType HandleFactor() throws IOException,CCompilingError
	{
		CType leftType = HandleSubFactor();
		CType rightType;
		while (true)
		{
			switch (its.GetToken())
			{
			case CPascalCompiler.TT_MUL:
				rightType = HandleSubFactor();
				if (leftType.GetTypeId() != rightType.GetTypeId())
				throw new CTypeNotMatchError();
				Emit(OP_MUL);
				break;
			case CPascalCompiler.TT_DIV:
				rightType = HandleSubFactor();
				if (leftType.GetTypeId() != rightType.GetTypeId())
				throw new CTypeNotMatchError();
				Emit(OP_DIV);
				break;
			case CPascalCompiler.TT_MOD:
				rightType = HandleSubFactor();
				if (leftType.GetTypeId() != rightType.GetTypeId())
				throw new CTypeNotMatchError();
				Emit(OP_MOD);
				break;
			case CPascalCompiler.TT_AND:
				rightType = HandleSubFactor();
				if (leftType.GetTypeId() != rightType.GetTypeId())
				throw new CTypeNotMatchError();
				Emit(OP_AND);
				break;
			default:
				its.pushBack();
				return leftType;
			}
		}
	}

    //Analyze factorregister_Stack
	//
	//	<Factor> -> <Constant> | <Variable> | (<Expression>) | not <Factor>
	//
	private CType HandleSubFactor() throws IOException, CCompilingError
	{
		CSymbol sym = null;
		CType type = null;
		switch (its.GetToken())
		{
		case CPascalCompiler.TT_WORD:
			sym = symbolTable.GetInfo(its.GetString());
			if (sym instanceof CConst)
			{
				Emit(OP_CONST);
				Emit(((CConst)sym).GetIntVal());
				if (((CConst)sym) instanceof CIntConst)
					type = new CIntType();
				else
				if (((CConst)sym) instanceof CBoolConst)
					type = new CBooleanType();
				else throw new CSyntaxError();
			}
			else
			{
				if (sym instanceof CVariable)
				{
					if (!IsSimpleType(type = GetVariableValue((CVariable)sym)))
						throw new CSyntaxError();
				}
				else throw new CSyntaxError();
			}
			break;
		case CPascalCompiler.TT_INTNUM:
			Emit(OP_CONST);
			Emit(its.ival);
			type = new CIntType();
			break;
		case '(':
			type = HandleExpression();
			if (its.GetToken() != ')')
				throw new CExpectedError(")");
			break;
		case CPascalCompiler.TT_NOT:
			type = HandleFactor();
			Emit(OP_NOT);
			break;
		}
		return type;
	}


    //Analyze expression
	//
	//	<Expression> -> <SimpleExpression> [<RelationalOperator> <SimpleExpression>]
	//
	//  <RelationalOperator> -> < | = | > | <= | >= | <>
	//
    private CType HandleExpression() throws IOException, CCompilingError
	{
		CType leftType = HandleSimpleExpression();
		CType rightType = null;
		switch (its.GetToken())
		{
		case TT_GT:
			if (!(leftType instanceof CIntType))
				throw new CSyntaxError();
			else
			{
				rightType = HandleSimpleExpression();
				if (leftType.GetTypeId() != rightType.GetTypeId())
					throw new CTypeNotMatchError();
				Emit(OP_GT);
			}
			return new CBooleanType();
		case TT_LT:
			if (!(leftType instanceof CIntType))
				throw new CSyntaxError();
			else
			{
				rightType = HandleSimpleExpression();
				if (leftType.GetTypeId() != rightType.GetTypeId())
					throw new CTypeNotMatchError();
				Emit(OP_LT);
			}
			return new CBooleanType();
		case TT_EQUAL:
			if (!(leftType instanceof CIntType))
				throw new CSyntaxError();
			else
			{
				rightType = HandleSimpleExpression();
				if (leftType.GetTypeId() != rightType.GetTypeId())
					throw new CTypeNotMatchError();
				Emit(OP_EQU);
			}
			return new CBooleanType();
		case TT_GE:
			if (!(leftType instanceof CIntType))
				throw new CSyntaxError();
			else
			{
				rightType = HandleSimpleExpression();
				if (leftType.GetTypeId() != rightType.GetTypeId())
					throw new CTypeNotMatchError();
				Emit(OP_NLT);
			}
			return new CBooleanType();
		case TT_LE:
			if (!(leftType instanceof CIntType))
				throw new CSyntaxError();
			else
			{
				rightType = HandleSimpleExpression();
				if (leftType.GetTypeId() != rightType.GetTypeId())
					throw new CTypeNotMatchError();
				Emit(OP_NGT);
			}
			return new CBooleanType();
		case TT_NE:
			if (!(leftType instanceof CIntType))
				throw new CSyntaxError();
			else
			{
				rightType = HandleSimpleExpression();
				if (leftType.GetTypeId() != rightType.GetTypeId())
					throw new CTypeNotMatchError();
				Emit(OP_NE);
			}
			return new CBooleanType();
		default:
			its.pushBack();
			if (leftType != null)
				return leftType;
			else throw new CExpectedError("Expression");
		}
	}

	//Test if T1 and T2 are equal (Can be changed into another)
	private boolean IsEqualType(CType t1, CType t2)
	{
		if (IsSimpleType(t1))
			if (t1.GetTypeId() == t2.GetTypeId()) return true; else return false;
		if (t1 == t2) return true;
		return false;
	}

	//Analyze the procedure calling statement
	//
	//	<STATEMENT> -> If <IF STATEMENT> |
	//				   While <WHILE STATEMENT> |
	//				   Begin <COMPOUND STATEMENT> |
	//				   <VARIABLE> <ASSIGN STATEMENT> |
	//				   <PROCEUDE> <PROCEUDE STATEMENT> |
	//				   <EMPTY STATEMENT>
	//	<EMPTY STATEMENT> -> ""
	//
	private void HandleStatement() throws IOException, CCompilingError
	{
		switch (its.GetToken())
		{
		case TT_WORD:
			if (its.ttype == TT_WORD)
			{
				CSymbol smb = symbolTable.GetInfo(its.GetString());
				if (smb instanceof CVariable)
					HandleAssignStatement((CVariable)smb);
				else if (smb instanceof CProcedure)
					HandleProcedureStatement((CProcedure)smb);
				else if (smb instanceof CReadProc)
					HandleReadStatement();
				else if (smb instanceof CWriteProc)
					HandleWriteStatement(((CWriteProc)smb).IsWriteln());
				else throw new CSyntaxError();
			}
			break;

		case TT_IF:
			HandleIfStatement();
			break;

		case TT_WHILE:
			HandleWhileStatement();
			break;

		case TT_SMCLN:
			its.pushBack();	//blank stament;
			break;

		case TT_BEGIN:
			HandleCompound();
			break;

		default:
			throw new CExpectedIdentifierError();
		}

	}

	//Analyze the Read procedure calling statement
	//
	//	<READ STATEMENT> -> '(' <VARIABLE> [{, <VARIABLE>}] ')'
	//
	private void HandleReadStatement() throws IOException,CCompilingError
	{
		its.GetSign('(');
		CType type = null;
		CSymbol smb = null;
		while (true)
		{
			its.GetIdentifier();
			smb = symbolTable.GetInfo(its.GetString());
			if (!(smb instanceof CVariable)) throw new CExpectedError("Variable");
			type = GetVariableAddress((CVariable)smb);
			if (!IsSimpleType(type)) throw new CNotSimpleVariable();
			Emit(OP_READ);
			if (its.GetToken() == ')') break;
			if (its.GetToken() != ',') throw new CExpectedError(",");
		}

	}

	//Analyze the Write procedure calling statement
	//	ln is the tag of if it's writeln
	//
	//	<WRITE STATEMENT> -> '(' <EXPRESSION> [{, <EXPRESSION>}] ')'
	//
	private void HandleWriteStatement(boolean ln) throws IOException,CCompilingError
	{
		its.GetSign('(');
		CType type;
		CSymbol smb;
		while (true)
		{
			type = HandleExpression();
			switch (type.GetTypeId())
			{
			case 0:
				throw new CNotSimpleVariable();
			case 1:
				Emit(OP_WRITEINT);
				break;
			case 2:
				Emit(OP_WRITEBOOL);
				break;
			}
			its.GetToken();
			if (its.ttype == ')') break;
			if (its.ttype != ',') throw new CExpectedError(",");
		}
	}

	//Analyze the procedure calling statement
	//
	//	<PROCEDURE STATEMENT> -> [ '(' <ARGUMENT> [{, <ARGUMENT>}] ')']
	//	<ARGUMENT> -> <EXPRESSION> | <IDENTIFIER>
	//
	private void HandleProcedureStatement(CProcedure proc) throws IOException,CCompilingError
	{
		int ac = proc.GetArgCount();
		CType type = null;
		CArgument arg = null;
		CSymbol smb;
		if (ac != 0)
		{
			its.GetSign('(');
			for (int i = 0; i < ac; i++)
			{
				arg = proc.GetItem(i);
				if (arg.varTag)//if it is Var argument
				{
					its.GetIdentifier();
					smb = symbolTable.GetInfo(its.GetString());
					if (!(smb instanceof CVariable)) throw new CExpectedError("Variable");
					type = GetVariableAddress((CVariable)smb);
				}
				else
				{
				    if (IsSimpleType(arg.GetType()))
				        type = HandleExpression();
				    else
				    {
				        its.GetIdentifier();
				        smb = symbolTable.GetInfo(its.GetString());
				        if (!(smb instanceof CVariable)) throw new CExpectedError("Variable");
				        type = GetVariableValue((CVariable)smb);

				    }
				}
				if (!IsEqualType(arg.GetType(), type)) throw new CTypeNotMatchError();
				if (i < ac - 1) its.GetSign(',');
			}
			its.GetSign(')');
		}
		int beginList = GetQuad();
		Emit(OP_CALL);
		Emit(currentLevel - proc.level);
		Emit(proc.GetOffset() - beginList);
	}

	//Analyze the assign statement
	//
	//	<ASSIGN STATEMENT> -> ":=" <EXPRESSION>
	//
	private void HandleAssignStatement(CVariable v) throws IOException, CCompilingError
	{
		CType type = GetVariableAddress(v);
		its.GetSign(TT_ASSIGN);
		if (IsSimpleType(type))
		{
			CType  t2 = HandleExpression();
			if (type.GetTypeId() != t2.GetTypeId()) throw new CTypeNotMatchError();
			Emit(OP_SMPASSIGN);
		}
		else
		{
			its.GetIdentifier();
			CSymbol smb = symbolTable.GetInfo(its.GetString());
			if (!(smb instanceof CVariable))
				throw new CExpectedError("variable type of "+type.toString());
			if (GetVariableValue((CVariable)smb) != type) throw new CTypeNotMatchError();
			Emit(OP_ASSIGN);
			Emit(type.GetSize());
		}
	}

	//Get the quad (the value of code pointer)
	private int GetQuad() { return codePointer;	}

	//Feed back the address offset in the codebuffer[addr]
	private void FeedBack(int addr, int v)
	{
		codeBuffer[addr] = v - addr + 1;	// + 1 Because OP_DO || OP_GOTO has a space
	}

	//Analyze the IF statement
	//
	//	<IF STATEMENT> -> <BOOLEAN EXPRESSION> THEN <STATEMENT> [ELSE <STATEMENT>]
	//
	private void HandleIfStatement() throws IOException, CCompilingError
	{
		if (HandleExpression().GetTypeId() != 2) throw new CNotBooleanExpressionError();
		Emit(OP_DO);
		int falseList = GetQuad();
		Emit(0);		//Waiting for feed back;
		its.GetSign(TT_THEN);
		HandleStatement();
		if (its.GetToken() == TT_ELSE)
		{
			Emit(OP_GOTO);
			int trueList = GetQuad();
			Emit(0);		//Waiting for feed back;
			FeedBack(falseList, GetQuad());
			HandleStatement();
			FeedBack(trueList, GetQuad());
		}
		else
		{
			its.pushBack();
			FeedBack(falseList, GetQuad());
		}
	};

	//Analyze the WHILE statement
	//
	//	<WHILE STATEMENT> -> <BOOLEAN EXPRESSION> DO <STATEMENT>
	//
	private void HandleWhileStatement() throws IOException, CCompilingError
	{
		int beginList = GetQuad();
		if (HandleExpression().GetTypeId() != 2) throw new CNotBooleanExpressionError();
		Emit(OP_DO);
		int falseList = GetQuad();
		Emit(0);		//Waiting for feed back;
		its.GetSign(TT_DO);
		HandleStatement();
		Emit(OP_GOTO);
		Emit(beginList - GetQuad() + 1);
		FeedBack(falseList, GetQuad());
	};

	//Analyze the WHILE statement
	//
	//	<COMPOUND STATEMENT> -> [<STATEMENT> [; {<STATEMENT>}] ';' ] End
	//
	private void HandleCompound() throws IOException, CCompilingError
	{
		while (true)
		{
			if (its.GetToken() == TT_END) return;
			its.pushBack();
			HandleStatement();
			its.GetToken();
			if (its.ttype == TT_END) return;
			if (its.ttype != TT_SMCLN) throw new CExpectedError(";");
		}
	}
}
